package com.ungao.framework.pool;

import com.ungao.framework.mapping.Environment;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MiniDataSource extends AbstractDataSource {

    /**
     * 数据库配置
     */
    private Environment environment;

    /**
     * 空闲连接池
     */
    private LinkedBlockingQueue<Connection> idleConnectionPool;

    /**
     * 活跃连接池
     */
    private LinkedBlockingQueue<Connection> activeConnectionPool;

    /**
     * 连接池锁
     */
    private Lock lock = new ReentrantLock();

    /**
     * 当前正在被使用的连接数,CAS原理
     */
    private AtomicInteger activeSize = new AtomicInteger(0);

    /**
     * 最大连接数
     */
    private final int maxSize;

    /**
     * 连接池只能有一个
     */
    private static volatile MiniDataSource singleInstance;

    /**
     * DCL 双重锁校验
     *
     * @param environment
     * @return
     */
    public static MiniDataSource getInstance(Environment environment) {
        if (singleInstance == null) {
            synchronized (MiniDataSource.class) {
                if (singleInstance == null) {
                    singleInstance = new MiniDataSource(environment);
                }
            }
        }
        return singleInstance;
    }

    private MiniDataSource(Environment environment) {
        this.environment = environment;
        this.maxSize = 2 << 4;
        this.idleConnectionPool = new LinkedBlockingQueue<>();
        this.activeConnectionPool = new LinkedBlockingQueue<>();
    }

    @Override
    public Connection getConnection() throws SQLException {

        // 从空闲连接池池中取出一个连接,如果有连接，则放入活跃连接池池中
        Connection connection = idleConnectionPool.poll();
        if (connection != null) {
            activeConnectionPool.offer(connection);
            return connection;
        }

        // 加锁
        lock.lock();
        try {

            // 如果空闲连接池池中连接未满maxSize，就新建一个连接
            if (activeSize.get() < maxSize) {
                if (activeSize.incrementAndGet() <= maxSize) {
                    Class.forName(environment.getDriver());
                    connection = DriverManager.getConnection(environment.getUrl(), environment.getUsername(), environment.getPassword());
                    activeConnectionPool.offer(connection);
                    return connection;
                }
            }

        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

        try {

            // 阻塞获取连接，如果10秒内有其他连接释放，
            connection = idleConnectionPool.poll(10000, TimeUnit.MILLISECONDS);
            if (connection == null) {
                throw new RuntimeException("等待连接超时");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return connection;
    }

    /**
     * 释放一个数据库连接
     *
     * @return
     */
    public void releaseConnection(Connection connection) {
        if (connection != null) {
            activeConnectionPool.remove(connection);
            idleConnectionPool.offer(connection);
        }
    }
}
